home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / VECTBAL2.ZIP / FIXEDL.ASM < prev    next >
Assembly Source File  |  1994-06-05  |  9KB  |  248 lines

  1. ; Fixed point routines.
  2. ; Tested with TASM 3.0.
  3.  
  4. USE386          equ     1       ;1 for 386-specific opcodes, 0 for
  5.                                                                 ; 8088 opcodes
  6. MUL_ROUNDING_ON equ     0       ;1 for rounding on multiplies,
  7.                                                                 ; 0 for no rounding. Not rounding is faster,
  8.                                                                 ; rounding is more accurate and generally a
  9.                                                                 ; good idea
  10. DIV_ROUNDING_ON equ     0       ;1 for rounding on divides,
  11.                                                                 ; 0 for no rounding. Not rounding is faster,
  12.                                                                 ; rounding is more accurate, but because
  13.                                                                 ; division is only performed to project to
  14.                                                                 ; the screen, rounding quotients generally
  15.                                                                 ; isn't necessary
  16. ALIGNMENT       equ     2
  17.  
  18.                 .model large
  19.                 .386
  20.                 .code
  21.  
  22. ;=====================================================================
  23. ; Multiplies two fixed-point values together.
  24. ; C near-callable as:
  25. ;       Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2);
  26. FMparms struc
  27.                 dd
  28.                 dw      ?        ;return address & pushed BP
  29.  
  30. M1      dd      ?
  31. M2      dd      ?
  32. FMparms ends
  33.                 align   ALIGNMENT
  34.                 public  _FixedMul
  35. _FixedMul       proc far
  36.                 push    bp
  37.                 mov     bp,sp
  38.  
  39. if USE386
  40.  
  41.                 mov     eax,[bp+M1]
  42.                 imul    dword ptr [bp+M2] ;multiply
  43. if MUL_ROUNDING_ON
  44.                 add     eax,8000h       ;round by adding 2^(-17)
  45.                 adc     edx,0           ;whole part of result is in DX
  46. endif ;MUL_ROUNDING_ON
  47.                 shr     eax,16          ;put the fractional part in AX
  48.  
  49. else    ;!USE386
  50.  
  51.                                                                 ;do four partial products and
  52.                                                                 ; add them together, accumulating
  53.                                                                 ; the result in CX:BX
  54.                 push    si              ;preserve C register variables
  55.                 push    di
  56.                                                                 ;figure out signs, so we can use
  57.                                                                 ; unsigned multiplies
  58.                 sub     cx,cx           ;assume both operands positive
  59.                 mov     ax,word ptr [bp+M1+2]
  60.                 mov     si,word ptr [bp+M1]
  61.                 and     ax,ax           ;first operand negative?
  62.                 jns     CheckSecondOperand ;no
  63.                 neg     ax              ;yes, so negate first operand
  64.                 neg     si
  65.                 sbb     ax,0
  66.                 inc     cx              ;mark that first operand is negative
  67. CheckSecondOperand:
  68.                 mov     bx,word ptr [bp+M2+2]
  69.                 mov     di,word ptr [bp+M2]
  70.                 and     bx,bx           ;second operand negative?
  71.                 jns     SaveSignStatus  ;no
  72.                 neg     bx              ;yes, so negate second operand
  73.                 neg     di
  74.                 sbb     bx,0
  75.                 xor     cx,1            ;mark that second operand is negative
  76. SaveSignStatus:
  77.                 push    cx              ;remember sign of result; 1 if result
  78.                                                                 ; negative, 0 if result nonnegative
  79.                 push    ax              ;remember high word of M1
  80.                 mul     bx              ;high word M1 times high word M2
  81.                 mov     cx,ax           ;accumulate result in CX:BX (BX not used
  82.                                                                 ; until next operation, however)
  83.                                                                 ;assume no overflow into DX
  84.                 mov     ax,si           ;low word M1 times high word M2
  85.                 mul     bx
  86.                 mov     bx,ax
  87.                 add     cx,dx           ;accumulate result in CX:BX
  88.                 pop     ax              ;retrieve high word of M1
  89.                 mul     di              ;high word M1 times low word M2
  90.                 add     bx,ax
  91.                 adc     cx,dx           ;accumulate result in CX:BX
  92.                 mov     ax,si           ;low word M1 times low word M2
  93.                 mul     di
  94. if MUL_ROUNDING_ON
  95.                 add     ax,8000h        ;round by adding 2^(-17)
  96.                 adc     bx,dx
  97. else ;!MUL_ROUNDING_ON
  98.                 add     bx,dx           ;don't round
  99. endif ;MUL_ROUNDING_ON
  100.                 adc     cx,0            ;accumulate result in CX:BX
  101.                 mov     dx,cx
  102.                 mov     ax,bx
  103.                 pop     cx
  104.                 and     cx,cx           ;is the result negative?
  105.                 jz      FixedMulDone    ;no, we're all set
  106.                 neg     dx              ;yes, so negate DX:AX
  107.                 neg     ax
  108.                 sbb     dx,0
  109. FixedMulDone:
  110.  
  111.                 pop     di              ;restore C register variables
  112.                 pop     si
  113.  
  114. endif   ;USE386
  115.  
  116.                 pop     bp
  117.                 ret
  118. _FixedMul       endp
  119.  
  120. ;=====================================================================
  121. ; Divides one fixed-point value by another.
  122. ; C near-callable as:
  123. ;       Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
  124. FDparms struc
  125.                 dd
  126.                 dw      ?        ;return address & pushed BP
  127. Dividend dd     ?
  128. Divisor  dd     ?
  129. FDparms ends
  130.                 align   ALIGNMENT
  131.                 public  _FixedDiv
  132. _FixedDiv       proc far
  133.                 push    bp
  134.                 mov     bp,sp
  135.  
  136. if USE386
  137.  
  138. if DIV_ROUNDING_ON
  139.                 sub     cx,cx           ;assume positive result
  140.                 mov     eax,[bp+Dividend]
  141.                 and     eax,eax         ;positive dividend?
  142.                 jns     FDP1            ;yes
  143.                 inc     cx              ;mark it's a negative dividend
  144.                 neg     eax             ;make the dividend positive
  145. FDP1:   sub     edx,edx         ;make it a 64-bit dividend, then shift
  146.                                                                 ; left 16 bits so that result will be
  147.                                                                 ; in EAX
  148.                 rol     eax,16          ;put fractional part of dividend in
  149.                                                                 ; high word of EAX
  150.                 mov     dx,ax           ;put whole part of dividend in DX
  151.                 sub     ax,ax           ;clear low word of EAX
  152.                 mov     ebx,dword ptr [bp+Divisor]
  153.                 and     ebx,ebx         ;positive divisor?
  154.                 jns     FDP2            ;yes
  155.                 dec     cx              ;mark it's a negative divisor
  156.                 neg     ebx             ;make divisor positive
  157. FDP2:   div     ebx             ;divide
  158.                 shr     ebx,1           ;divisor/2, minus 1 if the divisor is
  159.                 adc     ebx,0           ; even
  160.                 dec     ebx
  161.                 cmp     ebx,edx         ;set Carry if the remainder is at least
  162.                 adc     eax,0           ; half as large as the divisor, then
  163.                                                                 ; use that to round up if necessary
  164.                 and     cx,cx           ;should the result be made negative?
  165.                 jz      FDP3            ;no
  166.                 neg     eax             ;yes, negate it
  167. FDP3:
  168. else ;!DIV_ROUNDING_ON
  169.                 mov     edx,[bp+Dividend]
  170.                 sub     eax,eax
  171.                 shrd    eax,edx,16      ;position so that result ends up
  172.                 sar     edx,16          ; in EAX
  173.                 idiv    dword ptr [bp+Divisor]
  174. endif ;DIV_ROUNDING_ON
  175.                 shld    edx,eax,16      ;whole part of result in DX;
  176.                                                                 ; fractional part is already in AX
  177.  
  178. else ;!USE386
  179.  
  180. ;NOTE!!! Non-386 division uses a 32-bit dividend but only the upper 16 bits
  181. ; of the divisor; in other words, only the integer part of the divisor is
  182. ; used. This is done so that the division can be accomplished with two fast
  183. ; hardware divides instead of a slow software implementation, and is (in my
  184. ; opinion) acceptable because division is only used to project points to the
  185. ; screen (normally, the divisor is a Z coordinate), so there's no cumulative
  186. ; error, although there will be some error in pixel placement (the magnitude
  187. ; of the error is less the farther away from the Z=0 plane objects are). This
  188. ; is *not* a general-purpose divide, though; if the divisor is less than 1,
  189. ; for instance, a divide-by-zero error will result! For this reason, non-386
  190. ; projection can't be performed for points closer to the viewpoint than Z=1.
  191.  
  192.                                                                 ;figure out signs, so we can use
  193.                                                                 ; unsigned divisions
  194.                 sub     cx,cx           ;assume both operands positive
  195.                 mov     ax,word ptr [bp+Dividend+2]
  196.                 and     ax,ax           ;first operand negative?
  197.                 jns     CheckSecondOperandD ;no
  198.                 neg     ax              ;yes, so negate first operand
  199.                 neg     word ptr [bp+Dividend]
  200.                 sbb     ax,0
  201.                 inc     cx              ;mark that first operand is negative
  202. CheckSecondOperandD:
  203.                 mov     bx,word ptr [bp+Divisor+2]
  204.                 and     bx,bx           ;second operand negative?
  205.                 jns     SaveSignStatusD ;no
  206.                 neg     bx              ;yes, so negate second operand
  207.                 neg     word ptr [bp+Divisor]
  208.                 sbb     bx,0
  209.                 xor     cx,1            ;mark that second operand is negative
  210. SaveSignStatusD:
  211.                 push    cx              ;remember sign of result; 1 if result
  212.                                                                 ; negative, 0 if result nonnegative
  213.                 sub     dx,dx           ;put Dividend+2 (integer part) in DX:AX
  214.                 div     bx              ;first half of 32/16 division, integer part
  215.                                                                 ; divided by integer part
  216.                 mov     cx,ax           ;set aside integer part of result
  217.                 mov     ax,word ptr [bp+Dividend] ;concatenate the fractional part of
  218.                                                                 ; the dividend to the remainder (fractional
  219.                                                                 ; part) of the result from dividing the
  220.                                                                 ; integer part of the dividend
  221.                 div     bx              ;second half of 32/16 division
  222.  
  223. if DIV_ROUNDING_ON EQ 0
  224.                 shr     bx,1            ;divisor/2, minus 1 if the divisor is
  225.                 adc     bx,0            ; even
  226.                 dec     bx
  227.                 cmp     bx,dx           ;set Carry if the remainder is at least
  228.                 adc     ax,0            ; half as large as the divisor, then
  229.                 adc     cx,0            ; use that to round up if necessary
  230. endif ;DIV_ROUNDING_ON
  231.  
  232.                 mov     dx,cx           ;absolute value of result in DX:AX
  233.                 pop     cx
  234.                 and     cx,cx           ;is the result negative?
  235.                 jz      FixedDivDone    ;no, we're all set
  236.                 neg     dx              ;yes, so negate DX:AX
  237.                 neg     ax
  238.                 sbb     dx,0
  239. FixedDivDone:
  240.  
  241. endif ;USE386
  242.  
  243.                 pop     bp
  244.                 ret
  245. _FixedDiv       endp
  246.  
  247.                 end
  248.